package com.xfdmao.fcat.coin.base.util;

import com.alibaba.fastjson.JSONObject;
import com.xfdmao.fcat.coin.base.constant.KLineConstant;
import com.xfdmao.fcat.coin.base.entity.KlineInfo;
import com.xfdmao.fcat.coin.constant.CoinConstant;
import com.xfdmao.fcat.coin.entity.Kline;
import com.qwrt.base.common.util.DateUtil;
import org.apache.commons.beanutils.BeanUtils;

import java.lang.reflect.InvocationTargetException;
import java.math.BigDecimal;
import java.util.*;

/**
 * Created by cissa on 2019/7/27.
 */
public class KlineInfoUtil {
    /**
     * 清空K线列表中的收益率和买卖状态
     *
     * @param klineInfos
     */
    public static void emptyIncomeRateAndBuySellStatus(List<KlineInfo> klineInfos) {
        for (int i = 0; i < klineInfos.size(); i++) {
            KlineInfo klineInfo = klineInfos.get(i);
            klineInfo.setIncomeRate(0);
            klineInfo.setBuySellStatus(null);
        }
    }


    public static void print(List<KlineInfo> klineInfos, int upMa, Map<Date, Double> upAvgMap, int downMa, Map<Date, Double> downAvgMap) {
        System.out.println("总数：" + klineInfos.size());

        for (int i = 0; i < klineInfos.size(); i++) {
            KlineInfo klineInfo = klineInfos.get(i);

            System.out.println(String.format("%s" +
                            "\t%.4f" +
                            "\t%-8.4f" +
                            "\t%-8.4f" +
                            "\t%-8.4f" +
                            "\t%s" +
                            "\t%-8.4f",
                    DateUtil.formatDate(klineInfo.getDate(), DateUtil.TIME_PATTERN_DISPLAY), klineInfo.getClose(),
                    klineInfo.getGain() * 100,
                    upAvgMap.get(klineInfo.getDate()),
                    downAvgMap.get(klineInfo.getDate()),
                    klineInfo.getBuySellStatus(),
                    klineInfo.getIncomeRate() * 100
            ));
        }
        System.out.println("总收益：" + getSumIncomeRateRealList(klineInfos) * 100);
    }

    public static void print(List<KlineInfo> klineInfos) {
        System.out.println("总数：" + klineInfos.size());

        for (int i = 0; i < klineInfos.size(); i++) {
            KlineInfo klineInfo = klineInfos.get(i);

            System.out.println(String.format("%s" +
                            "\t%.4f" +
                            "\t%-8.4f" +
                            "\t%s" +
                            "\t%-8.4f" +
                            "\t%-8.4f"+
                            "\t%s",
                    DateUtil.formatDate(klineInfo.getDate(), DateUtil.TIME_PATTERN_DISPLAY), klineInfo.getClose(),
                    klineInfo.getGain() * 100,
                    klineInfo.getBuySellStatus(),
                    klineInfo.getIncomeRate() * 100,
                    klineInfo.getHighGain() * 100,
                    klineInfo.getBestHoldKlineNum()+""
            ));
        }
        System.out.println("总收益：" + getSumIncomeRateRealList(klineInfos) * 100);
    }

    /**
     * 处理数据:
     * 计算费率/
     *
     * @param klineInfos
     */
    public static void dealFee(List<KlineInfo> klineInfos) {
        double taker = CoinConstant.takerFee;
        for (int i = 0; i < klineInfos.size(); i++) {
            KlineInfo klineInfo = klineInfos.get(i);
            double incomeRate = klineInfo.getIncomeRate();
            incomeRate = new BigDecimal(incomeRate).subtract(new BigDecimal(taker)).doubleValue();
            klineInfo.setIncomeRate(incomeRate);
        }
    }

    /**
     * 返回每日的平均值
     *
     * @param klineInfos
     * @param avgNum
     * @return
     */
    public static Map<Date, Double> getAvg(List<KlineInfo> klineInfos, int avgNum) {
        Map<Date, Double> avg = new HashMap<>();
        for (int i = 0; i < klineInfos.size(); i++) {
            if (i < avgNum - 1) {
                avg.put(klineInfos.get(i).getDate(), 0d);
                continue;
            }
            BigDecimal sum = new BigDecimal(0);
            for (int j = i; j > i - avgNum; j--) {
                sum = sum.add(new BigDecimal(klineInfos.get(j).getClose()));
            }
            avg.put(klineInfos.get(i).getDate(), sum.divide(new BigDecimal(avgNum), 4, BigDecimal.ROUND_DOWN).doubleValue());
        }
        return avg;
    }


    /**
     * 将收益的K线对象列表转换成日期与计算收益的K线的Map映射
     *
     * @param klineInfos
     * @return
     */
    public static Map<Date, KlineInfo> getKLineMap(List<KlineInfo> klineInfos) {
        Map<Date, KlineInfo> kLineMap = new HashMap<>();
        for (int i = 0; i < klineInfos.size(); i++) {
            KlineInfo klineInfo = klineInfos.get(i);
            kLineMap.put(klineInfo.getDate(), klineInfo);
        }
        return kLineMap;
    }


    /**
     * 将火币返回的K线对象转换成代码要计算收益的K线对象列表
     *
     * @param kLineList
     * @return
     */
    public static List<KlineInfo> getKLines(List<Kline> kLineList) {
        List<KlineInfo> klineInfos = new ArrayList<>();
        for (Kline kline : kLineList) {
            KlineInfo klineInfo = new KlineInfo();
            try {
                BeanUtils.copyProperties(klineInfo, kline);
                klineInfo.setDate(kline.getKlineDate());
                klineInfo.setVolume(kline.getVol());
                klineInfos.add(klineInfo);
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            } catch (InvocationTargetException e) {
                e.printStackTrace();
            }
        }
        initData(klineInfos);
        return klineInfos;
    }

    /**
     * 判断K线是否长的下引线
     *
     * @param klineInfo
     * @return
     */
    public static boolean isDownLead(KlineInfo klineInfo, double leadFactor) {
        double downLead = getDownLeadGain(klineInfo);
        if (downLead > leadFactor) {
            return true;
        }
        return false;
    }

    /**
     * 判断K线是否长的上引线
     *
     * @param klineInfo
     * @return
     */
    public static boolean isUpperLead(KlineInfo klineInfo, double upperLeadFactor) {

        double upperLeadGain = getUpperLeadGain(klineInfo);
        if (upperLeadGain > upperLeadFactor) {
            return true;
        }
        return false;
    }


    public static double getSumIncomeRateRealList(List<KlineInfo> klineInfos) {
        double result = 0;
        for (KlineInfo klineInfo : klineInfos) {
            result += klineInfo.getIncomeRate();
        }
        return BigDecimal.valueOf(result).setScale(6, BigDecimal.ROUND_DOWN).doubleValue();
    }

    /**
     * 初始化涨幅，上引线的跌幅，下引线的涨幅
     *
     * @param klineInfos
     */
    public static void initData(List<KlineInfo> klineInfos) {
        double dayOpenValue = 0.0;
        for (int i = 0; i < klineInfos.size(); i++) {
            KlineInfo klineInfo = klineInfos.get(i);
            if(getDayOpen(klineInfo)!=null){
                dayOpenValue = getDayOpen(klineInfo);
            }

            klineInfo.setDayOpen(dayOpenValue);

            double gain = new BigDecimal(klineInfo.getClose()).subtract(new BigDecimal(klineInfo.getOpen())).divide(new BigDecimal(klineInfo.getOpen()), 6, BigDecimal.ROUND_DOWN).doubleValue();
            klineInfo.setGain(gain);

            double upperLeadGain = getUpperLeadGain(klineInfo);
            klineInfo.setUpLeadGain(upperLeadGain);

            double downLead = getDownLeadGain(klineInfo);
            klineInfo.setDownLeadGain(downLead);

            if (i > 0) {
                if (!BigDecimal.valueOf(klineInfos.get(i - 1).getVolume()).equals(BigDecimal.valueOf(0.0))) {
                    klineInfo.setVolRate(BigDecimal.valueOf(klineInfo.getVolume()).divide(BigDecimal.valueOf(klineInfos.get(i - 1).getVolume()), 6, BigDecimal.ROUND_DOWN).doubleValue());
                }
            }
        }
    }

    /**
     * 日期每天早上八点(2019-10-12 08:00)  开盘的价格
     * @param klineInfo
     * @return
     */

    public static Double getDayOpen(KlineInfo klineInfo) {
        Double dayOpenValue = null;
        if(DateUtil.formatDate(klineInfo.getDate(),DateUtil.TIME_PATTERN_DISPLAY).contains("08:00")){
            dayOpenValue = klineInfo.getOpen();
        }
        return dayOpenValue;
    }

    /**
     * 获取下引线的涨幅
     *
     * @param klineInfo
     * @return
     */
    private static double getDownLeadGain(KlineInfo klineInfo) {
        double min = klineInfo.getClose();//收盘和开盘最小
        if (klineInfo.getClose() > klineInfo.getOpen()) min = klineInfo.getOpen();
        return new BigDecimal(min).subtract(new BigDecimal(klineInfo.getLow())).divide(new BigDecimal(klineInfo.getLow()), 4, BigDecimal.ROUND_DOWN).doubleValue();
    }

    /**
     * 通过K线获取上引线的跌幅，为正值
     *
     * @param klineInfo
     * @return
     */
    private static double getUpperLeadGain(KlineInfo klineInfo) {
        double max = klineInfo.getClose();//收盘和开盘最大值
        if (klineInfo.getClose() < klineInfo.getOpen()) max = klineInfo.getOpen();
        return new BigDecimal(klineInfo.getHigh()).subtract(new BigDecimal(max)).divide(new BigDecimal(klineInfo.getHigh()), 4, BigDecimal.ROUND_DOWN).doubleValue();
    }

    public static boolean overGainFactory(List<KlineInfo> klineInfos, int i, Integer threeRaiseNum,double gainFactory) {
        boolean result = false;
        int start = i - threeRaiseNum;
        for (int j = start; j < i + 1; j++) {
            if (klineInfos.get(j).getGain()>=gainFactory) {
                result = true;
                break;
            }
        }
        return result;
    }


    /**
     * 最近20根收盘K线的下引线涨幅大于0.5个点
     * @param klineInfos
     * @param i
     * @param downLeadKlineCount
     * @param downLead
     * @return
     */
    public static boolean overDownLead(List<KlineInfo> klineInfos, int i, Integer downLeadKlineCount, double downLead) {
        boolean result = false;
        int start = i - downLeadKlineCount;
        for (int j = start; j < i + 1; j++) {
            if (klineInfos.get(j).getDownLeadGain()>=downLead && klineInfos.get(j).getGain()>0) {
                result = true;
                break;
            }
        }
        return result;
    }


    /**
     * 设置高低点涨幅, 设置最大获利的K线, 设置最佳的持仓时间
     * @param transactionKlineInfos
     * @param klineInfos
     */
    public static void dealHighGain(List<KlineInfo> transactionKlineInfos, List<KlineInfo> klineInfos) {
        for(int i=0;i<transactionKlineInfos.size();i++){
            KlineInfo transactionKlineInfo = transactionKlineInfos.get(i);
            //如果是当前的操作状态是买入平仓
            if(transactionKlineInfo.getBuySellStatus().equals(KLineConstant.BUYSELLSTATUS_BUY_CLOSE)){
                //买卖周期内的开始时间
                Date startDate = transactionKlineInfos.get(i-1).getDate();
                //买卖周期内的结束时间
                Date endDate = transactionKlineInfo.getDate();

                double sellOpenPrice = transactionKlineInfos.get(i-1).getClose();
                List<KlineInfo> startEndKlineInfos = KlineInfoUtil.getKLinesByStartEndDay(klineInfos,startDate,endDate);
                //debug到具体某一根K线
                if (transactionKlineInfos.get(i-1).getDate().getTime() == DateUtil.toDate("2019-07-14 00:45:00", DateUtil.TIME_PATTERN_DISPLAY).getTime()) {
                    System.out.println(JSONObject.toJSONString(transactionKlineInfos));
                }

                KlineInfo  lowerPriceKlineInfo = KlineInfoUtil.getLowerPriceKline(startEndKlineInfos);
                double lowerPrice = lowerPriceKlineInfo.getLow();
                if(lowerPriceKlineInfo.getDate().compareTo(startEndKlineInfos.get(0).getDate())==0){
                    lowerPrice = lowerPriceKlineInfo.getClose();
                }
                lowerPriceKlineInfo.setHighGainFlag(true);
                transactionKlineInfo.setBestHoldKlineNum(getBestHoldKlineNum(startEndKlineInfos,lowerPriceKlineInfo));

                transactionKlineInfo.setHighGain(BigDecimal.valueOf(sellOpenPrice).subtract(BigDecimal.valueOf(lowerPrice)).divide(BigDecimal.valueOf(sellOpenPrice),4,BigDecimal.ROUND_DOWN).doubleValue());
            }
        }
    }

    /**
     * 获取最佳的持仓时间
     * @param startEndKlineInfos
     * @param lowerPriceKlineInfo
     * @return
     */
    private static int getBestHoldKlineNum(List<KlineInfo> startEndKlineInfos, KlineInfo lowerPriceKlineInfo) {
        int i = 0;
        for(i=0;i<startEndKlineInfos.size();i++){
            if(startEndKlineInfos.get(i).getDate().compareTo(lowerPriceKlineInfo.getDate())==0){
                break;
            }
        }
        return i;
    }

    /**
     * 获取k线列表的最低值K线
     *
     * @param klineInfos
     * @return
     */
    private static KlineInfo getLowerPriceKline(List<KlineInfo> klineInfos) {
        KlineInfo result = klineInfos.get(0);
        double lowerPrice = 0;
        //开盘买入的k线不算
        for(int i=0;i<klineInfos.size();i++){
            KlineInfo klineInfo = klineInfos.get(i);
            if(i==0){
                lowerPrice = klineInfo.getClose();
            }else{
                if(klineInfo.getLow()<lowerPrice){
                    lowerPrice = klineInfo.getLow();
                    result = klineInfo;
                }
            }
        }
        return result;
    }


    /**
     * 通过开始时间和结束时间获取期间的K线列表
     * @param klineInfos
     * @param startDate
     * @param endDate
     * @return
     */
    private static List<KlineInfo> getKLinesByStartEndDay(List<KlineInfo> klineInfos, Date startDate, Date endDate) {
        List<KlineInfo> resultList = new ArrayList<>();
        for(int i=0;i<klineInfos.size();i++){
            KlineInfo klineInfo = klineInfos.get(i);
            if(klineInfo.getDate().compareTo(startDate)>=0 && klineInfo.getDate().compareTo(endDate)<=0){
                resultList.add(klineInfo);
            }
        }
        return resultList;
    }
}
